home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 2000-1 drscholl@users.sourceforge.net
- * This is free software distributed under the terms of the
- * GNU Public License. See the file COPYING for details.
- *
- * $Id: class.c,v 1.18 2001/02/15 08:39:45 drscholl Exp $
- *
- * Based on bans.c in part.
- * oracle ip database idea taken from ircd
- * is_address() taken from hybrid ircd and modified to return a complete
- * ip.
- * All this mess put together by Colten Edwards (q)
- */
-
- /* Modified 30/05/01 : Added include "extrasocket.h" with inlines for a few
- for Amiga port : bsdsocket.library functions not emulated by ixemul
-
- Added define for htonl(), which is not defined in the
- Amiga GCC 2.7.0 includes
- */
-
- #define htonl(x) (x)
-
- #include <ctype.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <limits.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include "opennap.h"
- #include "hashlist.h"
- #include "debug.h"
-
- #include "extrasocket.h"
-
- #define F_SYNC (1<<0)
-
- typedef struct
- {
- char *name;
- LIST *list;
- unsigned int flags;
- unsigned int oracle[256];
- }
- acl_t;
-
- static acl_t ilines;
- static acl_t dlines;
- static acl_t elines;
- static acl_t limits;
-
- #define check_oracle(acl,ip) (((acl)->oracle[(ip) & 0xff] & (ip)) == (ip))
-
- int
- check_class (CONNECTION * con, ip_info_t * info)
- {
- LIST *list;
- CLASS *class;
- int count, boot_em = 0;
-
- count = info->users;
-
- if (!count)
- return count;
-
- if (Max_Clones > 0 && count >= Max_Clones)
- boot_em = count;
-
- if (check_oracle (&limits, con->ip))
- {
- for (list = limits.list; list; list = list->next)
- {
- class = list->data;
- if ((con->ip & class->mask) == (class->target & class->mask))
- {
- if (count >= class->limit)
- return count;
- else
- return 0;
- }
- }
- }
- return boot_em;
- }
-
- /* determine whether a connection from this ip address is allowed by the acls
- * defined.
- */
- int
- acl_connection_allowed (unsigned int ip)
- {
- if (ilines.list)
- {
- /* i:lines exist, don't allow any connections unless the ip
- * matches
- */
- if (!check_oracle (&ilines, ip))
- return 0;
- }
-
- /* check for a d:line */
- if (check_oracle (&dlines, ip))
- {
- /* check for an e:line */
- if (check_oracle (&elines, ip))
- return 1;
-
- return 0;
- }
-
- return 1;
- }
-
- static int
- generic_acl_save (acl_t * acl)
- {
- FILE *fp;
- LIST *list;
- access_t *b;
- char path[_POSIX_PATH_MAX];
- char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
-
- snprintf (path, sizeof (path), "%s/%s", Config_Dir, acl->name);
-
- if (acl->list == 0)
- {
- unlink (path);
- return 0;
- }
-
- #ifdef WIN32
- #define LE "\r\n"
- #else
- #define LE "\n"
- #endif
-
- if ((fp = fopen (path, "w")) == 0)
- return -1;
- fprintf (fp,
- "# DO NOT EDIT THIS FILE! - automatically generated by opennap%s",
- LE);
- fprintf (fp, "# Hi Mom, We got %s!%s", acl->name, LE);
- for (list = acl->list; list; list = list->next)
- {
- struct in_addr in;
-
- b = list->data;
- maskstr[0] = 0;
- if (b->mask != 0xffffffff)
- {
- in.s_addr = BSWAP32 (b->mask);
- snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
- }
- in.s_addr = BSWAP32 (b->ip);
- fprintf (fp, "%s%s %3d%s", inet_ntoa (in), maskstr, b->count, LE);
- }
- if (fclose (fp))
- {
- logerr ("generic_acl_save", "fclose");
- return -1;
- }
- return 0;
- }
-
- void
- acl_save (void)
- {
- generic_acl_save (&limits);
- generic_acl_save (&ilines);
- generic_acl_save (&dlines);
- generic_acl_save (&elines);
- }
-
- static int
- compare_ipmask (unsigned int a, unsigned int amask, unsigned int b,
- unsigned int bmask)
- {
- int r = -2;
-
- /* always use the smallest mask to compare */
- unsigned int usemask = (amask > bmask) ? bmask : amask;
-
- /*
- struct in_addr in;
- in.s_addr = ntohl(a);
- printf("comparing %s(%u) and", inet_ntoa(in), a);
- in.s_addr = ntohl(b);
- printf(" %s(%u) = ",inet_ntoa(in), b);
- in.s_addr = ntohl(usemask);
- printf(" using mask=%s ", inet_ntoa(in));
- */
-
- if ((a & usemask) == (b & usemask))
- {
- /*printf("masks are equal "); */
- /* the larger mask (more specific) should sort first */
- if (bmask != amask)
- r = (amask > bmask) ? 1 : -1;
- }
-
- /* otherwise sort by ip */
- if (r == -2)
- {
- if (b == a)
- r = 0;
- else
- r = (b > a) ? 1 : -1;
- }
-
- /*
- printf("%d\n", r);
- */
-
- return r;
- }
-
- /** acl_insert
- * @param acl access list to modify
- * @param ipstr ip/mask string
- * @param nstr limit to place on class
- * @returns -1 on error, 0 if new acl was added, 1 if existing acl was changed
- */
- static int
- acl_insert (acl_t * acl, char *ipstr, int count)
- {
- unsigned int ip, mask;
- LIST **access_list = &acl->list;
- access_t *acc;
- LIST *list;
- int r;
-
- if (!is_address (ipstr, &ip, &mask))
- return -1;
-
- for (; *access_list; access_list = &(*access_list)->next)
- {
- acc = (*access_list)->data;
- r = compare_ipmask (htonl (BSWAP32 (ip)), htonl (BSWAP32 (mask)),
- htonl (BSWAP32 (acc->ip)),
- htonl (BSWAP32 (acc->mask)));
- if (r == 0)
- {
- acc->count = count;
- return 1;
- }
- else if (r > 0)
- break;
- }
- acc = CALLOC (1, sizeof (access_t));
- if (!acc)
- {
- OUTOFMEMORY ("acl_insert");
- return -1;
- }
- acc->ip = ip;
- acc->mask = mask;
- acc->count = count;
- list = CALLOC (1, sizeof (LIST));
- if (!list)
- {
- FREE (acc);
- OUTOFMEMORY ("acl_insert");
- return -1;
- }
- list->next = *access_list;
- *access_list = list;
- list->data = acc;
- acl->oracle[ip & 0xff] |= (ip | ~mask);
- return 0;
- }
-
- static int
- generic_acl_load (acl_t * acl)
- {
- FILE *fp;
- int ac;
- char *av[2], *ptr, path[_POSIX_PATH_MAX];
- int line = 0;
-
- snprintf (path, sizeof (path), "%s/%s", Config_Dir, acl->name);
-
- if (!(fp = fopen (path, "r")))
- {
- if (errno != ENOENT)
- logerr ("generic_acl_load", path);
- return -1;
- }
- while (fgets (Buf, sizeof (Buf) - 1, fp))
- {
- line++;
- ptr = Buf;
- while (ISSPACE (*ptr))
- ptr++;
- if (*ptr == '#' || *ptr == 0)
- continue;
- ac = split_line (av, FIELDS (av), Buf);
- if (ac < 1)
- continue;
-
- if (acl_insert (acl, av[0], (ac > 1) ? atoi (av[1]) : 0) == -1)
- {
- log ("generic_acl_load:%s:%d:error parsing line:%s %s",
- path, line, av[0], (ac > 1) ? av[1] : "");
- }
- }
- fclose (fp);
- return 0;
- }
-
- static acl_t *
- get_acl (int tag)
- {
- if (tag == MSG_CLIENT_DLINE_ADD || tag == MSG_CLIENT_DLINE_DEL
- || tag == MSG_CLIENT_DLINE_LIST)
- return &dlines;
- if (tag == MSG_CLIENT_ELINE_ADD || tag == MSG_CLIENT_ELINE_DEL
- || tag == MSG_CLIENT_ELINE_LIST)
- return &elines;
- if (tag == MSG_CLIENT_ILINE_ADD || tag == MSG_CLIENT_ILINE_DEL
- || tag == MSG_CLIENT_ILINE_LIST)
- return &ilines;
- if (tag == MSG_CLIENT_CLASS_ADD || tag == MSG_CLIENT_CLASS_DEL
- || tag == MSG_CLIENT_CLASS_LIST)
- return &limits;
- return 0;
- }
-
- static void
- generic_acl_init (acl_t * acl, const char *name, int flags)
- {
- memset (acl, 0, sizeof (acl));
- acl->name = STRDUP (name);
- acl->flags = flags;
- }
-
- void
- acl_init (void)
- {
- generic_acl_init (&limits, "limit", F_SYNC);
- generic_acl_load (&limits);
- generic_acl_init (&ilines, "iline", 0);
- generic_acl_load (&ilines);
- generic_acl_init (&dlines, "dline", 0);
- generic_acl_load (&dlines);
- generic_acl_init (&elines, "eline", 0);
- generic_acl_load (&elines);
- }
-
- /* ??? [:sender] <host>[/<mask>] [arg]
- */
- HANDLER (generic_acl_add)
- {
- int ac;
- char *av[2];
- char *sender_name;
- USER *sender;
- acl_t *acl;
- int count, modified = 0;
-
- (void) len;
- if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
- return;
- if (sender && sender->level < LEVEL_MODERATOR)
- return;
-
- ac = split_line (av, FIELDS (av), pkt);
- if (ac < 1)
- return;
-
- acl = get_acl (tag);
- count = (ac > 1) ? atoi (av[1]) : 0;
- modified = acl_insert (acl, av[0], count);
- if (modified == -1)
- {
- if (ISUSER (con))
- send_cmd (con, MSG_SERVER_NOSUCH, "unable to parse ip/mask");
- return;
- }
- notify_mods (CHANGELOG_MODE, "%s%s %s %s on %s (%d)",
- sender ? "" : "Server ",
- sender_name,
- modified ? "modified" : "added", acl->name, av[0], count);
-
- if (acl->flags & F_SYNC)
- pass_message_args (con, tag, ":%s %s %d", sender_name, av[0], count);
-
- generic_acl_save (acl);
- }
-
- /* ??? [:sender] <host>[/<mask>] */
- HANDLER (generic_acl_del)
- {
- char *host;
- unsigned int ip, mask;
- LIST **access_list;
- LIST *tmp;
- char *sender_name;
- USER *sender;
- access_t *acc;
- acl_t *acl;
-
- (void) len;
- if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
- return;
- if (sender && sender->level < LEVEL_MODERATOR)
- return;
- host = next_arg (&pkt);
- if (!host)
- return;
- if (!is_address (host, &ip, &mask))
- return;
- acl = get_acl (tag);
- access_list = &acl->list;
-
- if (!check_oracle (acl, ip))
- {
- if (ISUSER (con))
- send_cmd (con, MSG_SERVER_NOSUCH, "no matching ip addresses");
- return;
- }
-
- for (; *access_list; access_list = &(*access_list)->next)
- {
- acc = (*access_list)->data;
- if (mask == acc->mask && (ip & mask) == (acc->ip & acc->mask))
- {
- tmp = *access_list;
- *access_list = (*access_list)->next;
- FREE (tmp);
- break;
- }
- }
-
- /* rebuild the oracle */
- memset (acl->oracle, 0, sizeof (int) * 256);
-
- for (tmp = acl->list; tmp; tmp = tmp->next)
- {
- acc = tmp->data;
- acl->oracle[acc->ip & 0xff] |= (acc->ip | ~acc->mask);
- }
-
- notify_mods (CHANGELOG_MODE, "%s%s removed %s on %s",
- sender ? "" : "Server ", sender_name, acl->name, host);
-
- if (acl->flags & F_SYNC)
- pass_message_args (con, tag, ":%s %s", sender_name, host);
-
- generic_acl_save (acl);
- }
-
- /* ??? */
- HANDLER (generic_acl_list)
- {
- access_t *acc;
- acl_t *acl;
- char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
- struct in_addr in;
- LIST *list;
-
- (void) len;
- (void) pkt;
- CHECK_USER_CLASS ("generic_acl_list");
- if (con->user->level < LEVEL_MODERATOR)
- return;
- acl = get_acl (tag);
- for (list = acl->list; list; list = list->next)
- {
- acc = list->data;
-
- maskstr[0] = 0;
- if (acc->mask != 0xffffffff)
- {
- in.s_addr = BSWAP32 (acc->mask);
- snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
- }
-
- in.s_addr = BSWAP32 (acc->ip);
- send_cmd (con, tag, "%s%s %d", inet_ntoa (in), maskstr, acc->count);
- }
- send_cmd (con, tag, "");
- }
-
- static void
- generic_acl_sync (CONNECTION * con, int tag, acl_t * acl)
- {
- LIST *list;
- access_t *c;
- struct in_addr in;
- char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
-
- ASSERT (validate_connection (con));
- for (list = acl->list; list; list = list->next)
- {
- c = list->data;
- maskstr[0] = 0;
- if (c->mask != 0xffffffff)
- {
- in.s_addr = BSWAP32 (c->mask);
- snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
- }
- in.s_addr = BSWAP32 (c->ip);
- send_cmd (con, tag, ":%s %s%s %d", Server_Name,
- inet_ntoa (in), maskstr, c->count);
- }
- }
-
- void
- acl_sync (CONNECTION * con)
- {
- generic_acl_sync (con, MSG_CLIENT_CLASS_ADD, &limits);
- }
-
- static void
- acl_free (acl_t * acl)
- {
- list_free (acl->list, free_pointer);
- FREE (acl->name);
- }
-
- void
- acl_destroy (void)
- {
- acl_free (&limits);
- acl_free (&dlines);
- acl_free (&ilines);
- acl_free (&elines);
- }
-